/*
 * Copyright (c) 2020-2021. the original authors and DEPSEA.ORG
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.depsea.log.config;

import brave.Tracer;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.aop.Advice;
import org.depsea.log.advice.RequestAdvice;
import org.depsea.log.config.properties.LogPointAspectProperties;
import org.depsea.log.getter.ApiNameGetter;
import org.depsea.log.getter.UserGetter;
import org.depsea.log.getter.impl.Swagger3ApiNameGetterImpl;
import org.depsea.log.handler.ElasticsearchRequestPointHandler;
import org.depsea.log.handler.RequestLogHandler;
import org.depsea.log.handler.RequestPointHandler;
import org.depsea.log.helper.ExceptionElasticsearchLogHelper;
import org.depsea.log.notification.ExceptionNotification;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;

import java.util.List;

/**
 * 日志配置
 *
 * @author Wang Chengwei
 * @since 1.0.0
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({
        LogPointAspectProperties.class
})
@AutoConfigureAfter(ElasticsearchDataAutoConfiguration.class)
@Slf4j
public class RequestAopLogAutoConfiguration {

    private final LogPointAspectProperties logPointAspectProperties;

    public RequestAopLogAutoConfiguration(
            LogPointAspectProperties logPointAspectProperties) {
        this.logPointAspectProperties = logPointAspectProperties;
    }

    @Bean
    @ConditionalOnProperty(prefix = "spring.request-log", name = "enable", havingValue = "true")
    public Advisor requestAdvisor(Advice requestAdvice) {
        var pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(this.logPointAspectProperties.getPointcutExpression());
        return new DefaultPointcutAdvisor(pointcut, requestAdvice);
    }

    @Bean
    @ConditionalOnProperty(prefix = "spring.request-log", name = "enable", havingValue = "true")
    public Advice requestAdvice(
            Tracer tracer, List<RequestPointHandler> handlerList,
            @Autowired(required = false) ApiNameGetter apiNameGetter, @Autowired(required = false) UserGetter userGetter,
            @Autowired(required = false) List<ExceptionNotification> exceptionNotifications) {
        var requestAdvice = new RequestAdvice();
        requestAdvice.setTracer(tracer);
        requestAdvice.setApiNameGetter(apiNameGetter);
        requestAdvice.setUserGetter(userGetter);
        requestAdvice.setPointHandlers(handlerList);
        requestAdvice.setExceptionNotifications(exceptionNotifications);
        return requestAdvice;
    }

    @Bean
    @ConditionalOnMissingBean(ApiNameGetter.class)
    public ApiNameGetter apiNameGetter() {
        return new Swagger3ApiNameGetterImpl();
    }

    @Bean
    @ConditionalOnProperty(prefix = "spring.request-log", name = "enable-print-log", havingValue = "true", matchIfMissing = true)
    public RequestLogHandler requestLogHandler() {
        return new RequestLogHandler();
    }

    @ConditionalOnClass(ElasticsearchRestTemplate.class)
    @ConditionalOnProperty(prefix = "spring.request-log", name = "enable-write-to-es", havingValue = "true", matchIfMissing = true)
    @Configuration(proxyBeanMethods = false)
    @AutoConfigureAfter(ElasticsearchDataAutoConfiguration.class)
    public static class ElasticsearchPersistenceAutoConfiguration {

        public ElasticsearchPersistenceAutoConfiguration() {
            log.info("loading Elasticsearch");
        }

        @Bean
        @ConditionalOnBean(ElasticsearchRestTemplate.class)
        public ElasticsearchRequestPointHandler elasticsearchRequestPointHandler(
                ElasticsearchRestTemplate elasticsearchRestTemplate, LogPointAspectProperties logPointAspectProperties) {
            return new ElasticsearchRequestPointHandler(elasticsearchRestTemplate, logPointAspectProperties);
        }

        @Bean
        @ConditionalOnBean(ElasticsearchRestTemplate.class)
        public ExceptionElasticsearchLogHelper exceptionElasticsearchLogHelper(
                ElasticsearchRestTemplate elasticsearchRestTemplate, LogPointAspectProperties logPointAspectProperties) {
            return new ExceptionElasticsearchLogHelper(elasticsearchRestTemplate, logPointAspectProperties);
        }
    }

}
